﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DCT.CodeBox.SimplestStringTemplate.Codes
{
    /// <summary>
    /// 模板替换委托
    /// </summary>
    /// <param name="delimit">分隔符</param>
    /// <param name="key">键值</param>
    /// <returns>替换字符</returns>
    public delegate string SSTReplacement(char delimit, string key);
    
    /// <summary>
    /// 最简单的字符串模版引擎
    /// </summary>
    public class SST
    {
        #region 常用的Replacement

        public static SSTReplacement DictionaryReplacement(IDictionary<string, string> dict, string defaultVal)
        {
            return new SSTReplacement(delegate(char _delimit, string _key)
            {
                string val = "";
                if (!dict.TryGetValue(_key, out val))
                {
                    val = defaultVal;
                }
                return val;
            });
        }
        #endregion

        /// <summary>
        /// 空字符
        /// </summary>
        private static readonly char EMPTY_CHAR = default(char);

        /// <summary>
        /// 缓存操作锁
        /// </summary>
        /// <remarks>暂无用锁</remarks>
        private static object lockCache = new object();

        /// <summary>
        /// 解析缓存
        /// </summary>
        private static Dictionary<string, TemplateResolver> templateCaches
            = new Dictionary<string, TemplateResolver>();

        /// <summary>
        /// 清除解析缓存
        /// </summary>
        public static void CleanTemplateCache()
        {
            //lock (lockCache)
            //{
            templateCaches.Clear();
            //}
        }

        /// <summary>
        /// 模板输出文字，默认用一个#做分隔符
        /// </summary>
        /// <param name="template">模板字符串</param>
        /// <param name="replace">替换</param>
        /// <returns></returns>
        public static string ToString(string template, SSTReplacement replace)
        {
            return ToString(template, new char[] { '#' }, replace);
        }

        /// <summary>
        /// 模板输出文字
        /// </summary>
        /// <param name="template">模板字符串</param>
        /// <param name="delimit">单个键值分隔符</param>
        /// <param name="replace">替换</param>
        /// <returns>输出新字符串</returns>
        public static string ToString(string template, char delimit, SSTReplacement replace)
        {
            return ToString(template, new char[] { delimit }, replace);
        }

        /// <summary>
        /// 模板输出文字
        /// </summary>
        /// <param name="template">模板字符串</param>
        /// <param name="delimits">多个键值分隔符</param>
        /// <param name="replace">替换</param>
        /// <returns>输出新字符串</returns>
        public static string ToString(string template, char[] delimits, SSTReplacement replace)
        {
            //检验参数合法性
            if (delimits == null || delimits.Length == 0)
            {
                throw new InvalidOperationException("delimits is not allowed null nor empty.");
            }
            if (string.IsNullOrEmpty(template))
            {
                return "";
            }
            if (replace == null)
            {
                return template;
            }
            bool findDelimit = false;   //查找分隔符，如果分隔符不存在，则直接返回template
            foreach (char delimit in delimits)
            {
                if (template.IndexOf(delimit) > -1)
                {
                    findDelimit = true;
                    break;
                }
            }
            if (!findDelimit)
            {
                return template;
            }

            //尝试从缓存读取数据
            string cacheResult = "";
            if (ToStringFromCache(template, delimits, replace, out cacheResult))
            {
                return cacheResult;
            }

            //处理模板

            //初始化缓存
            TemplateResolver resolver = new TemplateResolver();
            resolver.CleanString = new List<string>();
            resolver.Keys = new List<TemplateResolverKey>();
            List<char> cleanString = new List<char>();

            //初始化解析变量
            Queue<char> stackTemplate = new Queue<char>(template);
            Queue<string> stackResults = new Queue<string>();
            char templateChar = EMPTY_CHAR;
            char matchDelimit = EMPTY_CHAR;
            List<char> matchs = new List<char>();

            while (stackTemplate.Count > 0)
            {
                templateChar = stackTemplate.Dequeue();
                if (Array.IndexOf(delimits, templateChar) >= 0)
                {
                    //遇到分隔符
                    if (matchDelimit == EMPTY_CHAR)
                    {
                        if (stackTemplate.Count == 0)                   //如果到了字符最后
                        {
                            stackResults.Enqueue(templateChar.ToString()); //输出，继续循环（停止循环）
                            cleanString.Add(templateChar);
                            continue;
                        }

                        if (stackTemplate.Peek() == templateChar)   //连续出现2个分隔符
                        {
                            stackResults.Enqueue(stackTemplate.Dequeue().ToString()); //输出一个分隔符
                            cleanString.Add(templateChar);
                            continue;
                        }

                        matchDelimit = templateChar;    //记录开始分隔符

                        resolver.CleanString.Add(new string(cleanString.ToArray()));
                        cleanString.Clear();

                    }
                    else
                    {
                        if (templateChar == matchDelimit)
                        {
                            //记录结束分隔符
                            resolver.CleanString.Add(null);   //记录空的给字符插入
                            cleanString.Clear();

                            string matchString = new string(matchs.ToArray());
                            stackResults.Enqueue(replace(matchDelimit, matchString) ?? "");


                            TemplateResolverKey resolverKey = new TemplateResolverKey();
                            resolverKey.Delimit = matchDelimit;
                            resolverKey.Key = matchString;
                            resolverKey.Pos = resolver.CleanString.Count - 1;
                            //置于最后，用倒序存储
                            resolver.Keys.Add(resolverKey);

                            //清空
                            matchDelimit = EMPTY_CHAR;
                            matchs.Clear();                 //清空match
                            continue;
                        }
                        else
                        {
                            matchs.Add(templateChar); //普通字符
                            continue;
                        }
                    }
                }
                else
                {
                    //不属于分隔符
                    if (matchDelimit == EMPTY_CHAR)
                    {
                        stackResults.Enqueue(templateChar.ToString());  //分隔符外
                        cleanString.Add(templateChar);
                    }
                    else
                    {
                        if (templateChar == '\\' && stackTemplate.Peek() == matchDelimit)
                        {
                            matchs.Add(stackTemplate.Dequeue());        //分隔符内，分隔符转义
                            continue;
                        }
                        matchs.Add(templateChar);                       //分隔符内
                    }
                }
            }

            resolver.CleanString.Add(new string(cleanString.ToArray()));
            cleanString.Clear();

            //添加缓存
            string cachekey = template + new string(delimits);
            templateCaches[cachekey] = resolver;


            return string.Concat(stackResults.ToArray());
        }

        /// <summary>
        /// 从缓存读取解析模板
        /// </summary>
        /// <param name="template"></param>
        /// <param name="delimits"></param>
        /// <param name="replace"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        private static bool ToStringFromCache(string template, char[] delimits
            , SSTReplacement replace, out string result)
        {
            bool succ = false;
            string cachekey = template + new string(delimits);
            TemplateResolver resolver = default(TemplateResolver);
            result = "";

            if (templateCaches.TryGetValue(cachekey, out resolver))
            {
                string[] clean = new string[resolver.CleanString.Count];
                resolver.CleanString.CopyTo(clean, 0);
                foreach (TemplateResolverKey key in resolver.Keys)
                {
                    //result = result.Insert(key.Pos, replace(key.Delimit, key.Key));
                    clean[key.Pos] = replace(key.Delimit, key.Key);
                }
                succ = true;
                result = string.Concat(clean);
            }

            return succ;
        }

        /// <summary>
        /// 模板解析缓存
        /// </summary>
        struct TemplateResolver
        {
            /// <summary>
            /// 模板字符串过滤键值後的字符串
            /// </summary>
            public List<string> CleanString;

            /// <summary>
            /// 键值
            /// </summary>
            public List<TemplateResolverKey> Keys;
        }

        /// <summary>
        /// 模板解析键值
        /// </summary>
        struct TemplateResolverKey
        {
            /// <summary>
            /// 键值所处在CleanString的位置
            /// </summary>
            public int Pos;
            /// <summary>
            /// 分隔符
            /// </summary>
            public char Delimit;
            /// <summary>
            /// 键值
            /// </summary>
            public string Key;
        }
    }
}
